home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 13 - 1997 (partial) / 13.02 Feb 97 / HappyFace, a CTB Game / CTB.c next >
Encoding:
Text File  |  1996-10-17  |  19.1 KB  |  780 lines  |  [TEXT/CWIE]

  1. /* CTB.c - Handles action specific to the Communications Toolbox 
  2.  
  3. This file can be included in a C project or compiled as a 
  4. library.  It allows the programmer to initiate connections
  5. through a Communications Toolbox connection.  In theory, 
  6. It works with any connection tool.  It was most rigorously
  7. tested with the Appletalk ADSP tool.
  8.  
  9. Assumptions: 
  10.     You are running a machine and OS that has/supports
  11.         Communications Toolbox.
  12.     You will not attempt to cram more into the CTB buffers
  13.         than they can hold.
  14.     You will not stress-test the Asynchronous calls, which
  15.         may not be ready for prime time.
  16. */
  17.  
  18. // Necessary includes (uncomment or precompile)
  19. #include "Connections.h"
  20. #include "FileTransfers.h"
  21. #include "CommResources.h"
  22. #include "CTB.h"
  23.  
  24. // Private literals
  25. #define kPrefsType                'PREF'    // signature resource is
  26. #define kAppSignature            'FACE'    // registered w/Apple
  27. #define kClassCM                'cbnd'    // replace with your own
  28. #define rConnClassID            1001
  29. #define rFirstToolConfigID        2001
  30.  
  31. // Global variables
  32. static ConnHandle                 gConn = nil;
  33. static Boolean                    gAsyncCalls, gCTBInited = false;
  34. static CMFlags                    gFlags;
  35. static CMStatFlags                gPrevStatus;
  36. static MenuHandle                gConnMenu;
  37. static ConnectionCompletionUPP    gCMCompletion;
  38. static FileTransferSendUPP        gCMWriteProc;
  39. static FileTransferReceiveUPP    gCMReadProc;
  40.  
  41. //**********************************************************
  42. //
  43. // pStrCopy - Copy the value of p1 to p2
  44. //
  45. //**********************************************************
  46. static void pStrCopy(unsigned char *p1, unsigned char *p2)
  47. {
  48.     register short len = *p2++ = *p1++;
  49.     while (--len >= 0)
  50.         *p2++=*p1++;
  51. }
  52.  
  53. //**********************************************************
  54. //
  55. // GetOpenFileDir - Gets VRef and ParentDirId for open file
  56. //
  57. //**********************************************************
  58. static OSErr GetOpenFileDir(short openFileRef, short *vRef, 
  59.     long *parDirID)
  60. {
  61.     FCBPBRec pb;
  62.     OSErr theErr;
  63.         
  64.     pb.ioCompletion = nil;
  65.     pb.ioFCBIndx = 0;
  66.     pb.ioVRefNum = 0;
  67.     pb.ioRefNum = openFileRef;
  68.     pb.ioNamePtr = nil;
  69.     
  70.     theErr = PBGetFCBInfo(&pb, false);
  71.     
  72.     *vRef = pb.ioFCBVRefNum;
  73.     *parDirID = pb.ioFCBParID;
  74.     
  75.     return theErr;
  76. }
  77.  
  78. //**********************************************************
  79. //
  80. // OpenPrefsFile - Opens prefs, creates if not found
  81. //
  82. //**********************************************************
  83. static OSErr OpenPrefsFile(short *myRefNum, Boolean createIt)
  84. {
  85.     Str255    myFileName;
  86.     long    myDirID;
  87.     short    myVRef, appResRef;
  88.     OSErr    theErr;
  89.     FSSpec    mySpec, appDirSpec;
  90.     Boolean    found;
  91.         
  92.     *myRefNum = -1;
  93.     found = false;
  94.     pStrCopy("\pHappyFace Prefs", myFileName);
  95.     appResRef = CurResFile();
  96.  
  97.     theErr = GetOpenFileDir(appResRef, &myVRef, &myDirID);
  98.     if (!theErr) {
  99.         theErr = FSMakeFSSpec(myVRef, myDirID, myFileName, 
  100.             &appDirSpec);
  101.         if (!theErr) {
  102.             found = true;
  103.             BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
  104.         }
  105.     }
  106.         
  107.     if ( (theErr == fnfErr) && createIt ) {
  108.         BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
  109.         FSpCreateResFile(&mySpec, kAppSignature, kPrefsType, 0);
  110.         theErr = ResError();
  111.     }
  112.     
  113.     if (!theErr) {
  114.         *myRefNum = FSpOpenResFile(&mySpec, fsRdWrPerm);
  115.         theErr = ResError();
  116.     }
  117.     
  118.     return theErr;
  119. }
  120.  
  121. //**********************************************************
  122. //
  123. // CMCompetion - Completion routine for the Connection Mgr
  124. //
  125. //**********************************************************
  126. static pascal void CMCompletion(ConnHandle hConn)
  127. {
  128.     // Should at least check to see if (**hConn).errCode 
  129.     // is non-zero.  Perhaps use hConn to put any error 
  130.     // code in UserData for the main program to flush out.
  131. }
  132.  
  133. //**********************************************************
  134. //
  135. // CMWriteProc - Sends the data out through the connection
  136. //
  137. //**********************************************************
  138. static pascal OSErr CMWriteProc(Ptr thePtr, long theSize, 
  139.     CMFlags flags)
  140. {
  141.     long            count, returnVal;
  142.     EventRecord        theEvent;
  143.     CMBufferSizes    sizes;
  144.     Ptr                p;
  145.     OSErr            theErr;
  146.     CMStatFlags        status;
  147.     
  148.     returnVal = 0;
  149.     
  150.     if (gConn) {
  151.         p = thePtr;
  152.         count = theSize;// Give other processes time while
  153.         do {        // waiting for previous write to finish
  154.             if (gAsyncCalls) do {
  155.                 if (theErr = CMStatus(gConn, sizes, &status))
  156.                     return theErr;
  157.                 if (status & (cmStatusDWPend + cmStatusOpening))
  158.                     WaitNextEvent(nullEvent, &theEvent, 1, nil);
  159.             } while (status & (cmStatusDWPend + cmStatusOpening));
  160.             if (theErr = CMWrite(gConn, p, &count, cmData,
  161.                     gAsyncCalls, gCMCompletion, -1, flags))
  162.                 return theErr;
  163.             if (gAsyncCalls) {
  164.                 do { // async calls provide false count
  165.                     if (theErr = CMStatus(gConn, sizes, &status))
  166.                         return theErr;
  167.                     if (status & (cmStatusDWPend + cmStatusOpening))
  168.                         WaitNextEvent(nullEvent, &theEvent, 1, nil);
  169.                 } while (status & cmStatusDWPend);
  170.                 count = (**gConn).asyncCount[cmDataOut];
  171.             }
  172.             p += count;    // continue where we left off
  173.             theSize = theSize - count;
  174.             count = theSize;
  175.         } while (count > 0);
  176.     }
  177.     return noErr;
  178. }
  179.  
  180. //**********************************************************
  181. //
  182. // ConnEvent - Give the Connection a crack at the event
  183. //
  184. //**********************************************************
  185. Boolean ConnEvent(EventRecord *theEvent)
  186.     WindowPtr    theWindow;
  187.         
  188.     switch(theEvent->what) {
  189.         case autoKey:
  190.         case keyDown:
  191.             theWindow = FrontWindow();
  192.             break;
  193.         case mouseDown:
  194.             FindWindow(theEvent->where, &theWindow);
  195.             break;
  196.         case updateEvt:
  197.         case activateEvt:
  198.             theWindow = (WindowPtr)theEvent->message;
  199.             break;
  200.         default:
  201.             theWindow = nil; // only pass through above events
  202.             break;
  203.     }
  204.  
  205.     if (!gConn || !theWindow)
  206.         return false;
  207.     else if (gConn != (ConnHandle)GetWRefCon(theWindow))
  208.         return false;
  209.     else {
  210.         CMEvent(gConn, theEvent);
  211.         return true;
  212.     }
  213. }
  214.  
  215. //**********************************************************
  216. //
  217. // ConnActivate - Passes activate event on to Connection
  218. //
  219. //**********************************************************
  220. void ConnActivate(WindowPtr theWindow, Boolean becomingActive)
  221. {
  222.     GrafPtr    oldPort;
  223.         
  224.     if (gConn && theWindow) {
  225.         GetPort(&oldPort);
  226.         SetPort(theWindow);
  227.         CMActivate(gConn, becomingActive);
  228.         SetPort(oldPort);
  229.     }
  230. }
  231.  
  232. //**********************************************************
  233. //
  234. // ConnResume - Passes resume event on to Connection
  235. //
  236. //**********************************************************
  237. void ConnResume(WindowPtr theWindow, Boolean resuming)
  238. {    
  239.     GrafPtr oldPort;
  240.     
  241.     if (gConn && theWindow) {
  242.         GetPort(&oldPort);
  243.         SetPort(theWindow);
  244.         CMResume(gConn, resuming);
  245.         SetPort(oldPort);
  246.     }
  247. }
  248.  
  249. //**********************************************************
  250. //
  251. // putc - Sends a character out through the connection
  252. //
  253. //**********************************************************
  254. OSErr putc(unsigned char *c)
  255. {
  256.     return CMWriteProc((Ptr)c, 1L, gFlags);
  257. }
  258.  
  259. //**********************************************************
  260. //
  261. // CMReadProc - Reads data in from the connection
  262. //
  263. //**********************************************************
  264. static pascal OSErr CMReadProc(Ptr thePtr, long *theSize, 
  265.     CMFlags *flags)
  266.     // reads *theSize chars into dataPtr
  267.     // synchronous, no matter what gAsyncCalls says;
  268.     // will have to try harder to get the contents of a block
  269.     if (gConn)
  270.         return CMRead(gConn, thePtr, theSize, cmData, false, 
  271.             nil, 0, flags);
  272.     else {
  273.         *theSize = 0;
  274.         return noErr;
  275.     }
  276. }
  277.  
  278. //**********************************************************
  279. //
  280. // getc - gets a character in from the connection
  281. //
  282. //**********************************************************
  283. OSErr getc(unsigned char *c)
  284. {
  285.     OSErr    theErr;
  286.     long    theLen;
  287.     CMFlags    flags;
  288.     
  289.     theLen = 1;
  290.     theErr = CMReadProc((Ptr)c, &theLen, &flags);
  291.     if (!theLen)
  292.         *c = NULL;
  293.     return theErr;
  294. }
  295.  
  296. //**********************************************************
  297. //
  298. // InitCTBStuff - Initializes comm toolbox
  299. //
  300. //**********************************************************
  301. static OSErr InitCTB(void)
  302. {
  303.     short             theErr;
  304.  
  305.     if (!gCTBInited) {
  306.         if (theErr = InitCTBUtilities())
  307.             return theErr;
  308.         if (theErr = InitCRM())
  309.             return theErr;
  310.         if (theErr = InitCM())
  311.             return theErr;
  312.         // PowerPC friendly Mixed-Mode stuff follows
  313.         gCMCompletion = (ConnectionCompletionUPP)
  314.             NewConnectionCompletionProc((ProcPtr)CMCompletion);
  315.         gCMWriteProc = (FileTransferSendUPP)
  316.             NewFileTransferSendProc((ProcPtr)CMWriteProc);
  317.         gCMReadProc = (FileTransferReceiveUPP)
  318.             NewFileTransferReceiveProc((ProcPtr)CMReadProc);
  319.         gCTBInited = true;
  320.     }
  321.     return noErr;
  322. }
  323.  
  324. //**********************************************************
  325. //
  326. // FindTool - Tries to get the choice tool if 
  327. //                available, else tries first available.
  328. //
  329. //**********************************************************
  330. static OSErr FindTool(short *procID, Str255 toolName)
  331. {
  332.     Handle            rHand;
  333.     StringHandle    sH;
  334.     OSErr            theErr;
  335.  
  336.     *procID = -1;
  337.     theErr = noErr;
  338.     
  339.     // Get name of tool last used, and its procID
  340.     rHand = GetResource(kAppSignature, rConnClassID);
  341.     if (rHand) {
  342.         sH = (StringHandle)rHand;
  343.         pStrCopy(*sH, toolName);
  344.         *procID = CMGetProcID(toolName);
  345.     }
  346.     if (*procID == -1 ) {
  347.         // may not actually have a resource file open, or the
  348.         // tool we want is no longer in the extensions folder so
  349.         // get the name and procID of the first tool we find
  350.         if (!(theErr = CRMGetIndToolName(kClassCM, 1, toolName)))
  351.             *procID = CMGetProcID(toolName);
  352.         if (*procID == -1)
  353.             theErr = ResError();
  354.         if (!theErr)
  355.             theErr = kToolNotAvailErr;
  356.     }
  357.     return theErr;
  358. }
  359.  
  360. //**********************************************************
  361. //
  362. // ReadPrefs - Read prefs for tool of specified class
  363. //
  364. //**********************************************************
  365. static OSErr ReadPrefs(short *procID, Handle *configH)
  366. {
  367.     short     resFileRef;
  368.     Str255    toolName;
  369.     Handle    resH;
  370.     OSErr    theErr;
  371.     
  372.     theErr = OpenPrefsFile(&resFileRef, kDontCreate);
  373.     // if we can't open prefs, we can still get a tool
  374.     theErr = FindTool(procID, toolName);
  375.     *configH = nil;
  376.     
  377.     if (resFileRef != -1) {
  378.         if (!theErr) {
  379.             // if the resource file was opened, get the 
  380.             // configuration string from a resource
  381.             resH = GetNamedResource(kAppSignature, toolName);
  382.             if (resH) {
  383.                 *configH = resH;
  384.                 HandToHand(configH);
  385.                 ReleaseResource(resH);
  386.             }  else
  387.                 theErr = ResError();
  388.         }
  389.         CloseResFile(resFileRef);
  390.     }
  391.     return theErr; // could be returning kToolNotAvailErr
  392. }
  393.  
  394. //**********************************************************
  395. //
  396. // WritePrefs - Create/Update connection tool prefs
  397. //
  398. //**********************************************************
  399. static OSErr WritePrefs(ConnHandle hConn)
  400. {
  401.     Ptr                specP;
  402.     Handle            specH, resH;
  403.     Str255            tName, tClassName;
  404.     OSErr            theErr;
  405.     short            resID, resFileRef;
  406.     Size            hSize;
  407.     StringHandle    sH;
  408.     
  409.     if (!hConn)
  410.         return kNoConnectionErr;
  411.     
  412.     // Get the current tool's name and its config string
  413.     CMGetToolName((**hConn).procID, tName);
  414.     specP = CMGetConfig(hConn);
  415.     if (!specP)
  416.         return kGetConfigErr;
  417.     
  418.     if (theErr = OpenPrefsFile(&resFileRef, kDoCreate))
  419.         return theErr;
  420.             
  421.     // Update/Create resource specifying tool name
  422.     resID = rConnClassID;
  423.     pStrCopy("\pConnection Tool Name", tClassName);
  424.     sH = NewString(tName);
  425.     if (sH) {
  426.         hSize = GetHandleSize((Handle)sH);
  427.         resH = Get1Resource(kAppSignature, resID);
  428.         theErr = ResError();
  429.         if ((!resH) && ((theErr == resNotFound) || 
  430.                 (theErr == noErr))) {
  431.             AddResource((Handle)sH, kAppSignature, 
  432.                 resID, tClassName);
  433.             if (!(theErr = ResError()))
  434.                 WriteResource((Handle)sH);
  435.             if (!theErr)
  436.                 theErr = ResError();
  437.             ReleaseResource((Handle)sH);
  438.         } else if (resH) {
  439.             SetHandleSize(resH, hSize);
  440.             if (!(theErr = MemError())) {
  441.                 BlockMove(*sH, *resH, hSize);
  442.                 ChangedResource(resH);
  443.                 if (!(theErr = ResError()))
  444.                     WriteResource(resH);
  445.                 if (!theErr)
  446.                     theErr = ResError();
  447.             }
  448.             ReleaseResource(resH);
  449.         }
  450.         DisposeHandle((Handle)sH);
  451.     } else
  452.         theErr = MemError();
  453.     
  454.     // Update/Create resource with the tool config string
  455.     if (!theErr) {
  456.         hSize = GetPtrSize(specP);
  457.         resH = Get1NamedResource(kAppSignature, tName);
  458.         theErr = ResError();
  459.         if ( (!resH) && ((theErr == resNotFound) || 
  460.                 (theErr == noErr)) ) {
  461.             theErr = noErr;
  462.             specH = NewHandle(hSize);
  463.             if (specH) {
  464.                 BlockMove(specP, *specH, hSize);
  465.                 do
  466.                     resID = UniqueID(kAppSignature);
  467.                 while (resID <= rFirstToolConfigID);
  468.                 AddResource(specH, kAppSignature, resID, tName);
  469.                 if (!(theErr = ResError()))
  470.                     WriteResource(specH);
  471.                 if (!theErr)
  472.                     theErr = ResError();
  473.                 ReleaseResource(specH);
  474.                 DisposeHandle(specH);
  475.             } else
  476.                 theErr = MemError();
  477.         } else if (resH) {
  478.             SetHandleSize(resH, hSize);
  479.             if (!(theErr = MemError())) {
  480.                 BlockMove(specP, *resH, hSize);
  481.                 ChangedResource(resH);
  482.                 if (!(theErr = ResError()))
  483.                     WriteResource(resH);
  484.                 if (!theErr)
  485.                     theErr = ResError();
  486.             }
  487.             ReleaseResource(resH);
  488.         }
  489.     }
  490.     
  491.     DisposePtr(specP);
  492.     CloseResFile(resFileRef);
  493.     
  494.     return theErr;
  495. }
  496.  
  497. //**********************************************************
  498. //
  499. // ChooseConn - Poses Connection Settings dialog
  500. //        allowing the user to choose and configure a tool
  501. //
  502. //**********************************************************
  503. OSErr ChooseConn(void)
  504. {
  505.     Point    where;
  506.     short    result;
  507.     OSErr    returnVal;
  508.     
  509.     returnVal = kNothingChosenErr;
  510.     
  511.     if (gConn) {
  512.         SetPt(&where, 20, 40);
  513.         HUnlock((Handle)gConn);
  514.         result = CMChoose(&gConn, where, nil);
  515.         HLock((Handle)gConn);
  516.         switch (result) {
  517.             case chooseOKMinor:
  518.             case chooseOKMajor:
  519.                 returnVal = noErr;
  520.                 break;
  521.             case chooseCancel:
  522.                 returnVal = kNothingChosenErr;
  523.                 break;
  524.             case chooseAborted:
  525.             case chooseDisaster:
  526.             case chooseFailed:
  527.             default:
  528.                 returnVal = result;
  529.                 break;
  530.         }
  531.     }
  532.     return returnVal;
  533. }
  534.  
  535. //**********************************************************
  536. //
  537. //    NewConn - Init connection mgr for use
  538. //
  539. //**********************************************************
  540. OSErr NewConn(Boolean async, Boolean initCTB, Boolean prompt)
  541. {
  542.     short            procID;
  543.     Handle            configH;
  544.     CMBufferSizes    sizes;
  545.     OSErr             theErr;
  546.     Boolean            newTool;
  547.     
  548.     if (gConn)
  549.         return kAlreadyInitErr;
  550.     
  551.     if (initCTB)
  552.         if (theErr = InitCTB())
  553.             return theErr;
  554.     
  555.     // Find user's preferred tool and config string
  556.     // or get defaults if blown or do not exist
  557.     theErr = ReadPrefs(&procID, &configH);
  558.     if (theErr == kToolNotAvailErr) {
  559.         newTool = true;
  560.         theErr = noErr;    // if problem is tool changed,
  561.     } else if (theErr)    // make note, else pitch
  562.         return theErr;
  563.     else
  564.         newTool = false;
  565.         
  566.     if (procID == -1)
  567.         return kNoConnToolErr;
  568.         
  569.     sizes[cmDataIn] = 0; // Asking for zero means leave 
  570.     sizes[cmDataOut] = 0; // buffer size up to tool
  571.     sizes[cmCntlIn] = 0;
  572.     sizes[cmCntlOut] = 0;
  573.     sizes[cmAttnIn] = 0;
  574.     sizes[cmAttnOut] = 0;
  575.     
  576.     // Get a new Connection Tool record
  577.     // and set config string as saved
  578.     gConn = CMNew(procID, cmData, sizes, 0, 0);
  579.     if (!gConn) {
  580.         if (configH)
  581.             DisposeHandle(configH);
  582.         return kCMNewErr;
  583.     } else if (configH) {
  584.         HLock(configH);     // don't let memory manager move  
  585.         CMSetConfig(gConn, *configH);    // dereferenced pointer
  586.         HUnlock(configH);
  587.         DisposeHandle(configH);
  588.     }
  589.     MoveHHi((Handle)gConn);
  590.     HLock((Handle)gConn);     // lock to access data at interrupt
  591.     
  592.     // pose Connection Settings dialog if desired or desired 
  593.     // tool or configuration string was not available
  594.     theErr = noErr;
  595.     if (prompt || newTool || !configH)
  596.         if (ChooseConn())
  597.             theErr = kNothingChosenErr;
  598.  
  599.     if (theErr) {
  600.         if (gConn)    // if ChooseDisaster, gConn already 
  601.             CMDispose(gConn);    // disposed & nil
  602.         gConn = nil;
  603.         return theErr;
  604.     }
  605.  
  606.     gFlags = false;
  607.     gAsyncCalls = async;
  608.     gPrevStatus = 0xFFFFFFFF;
  609.     gConnMenu = GetMenu(rConnMenuID);
  610.         
  611.     return noErr;
  612. }
  613.  
  614. //**********************************************************
  615. //
  616. //    OpenConn - Open connection with other copy
  617. //
  618. //**********************************************************
  619. OSErr OpenConn(void)
  620. {
  621.     if (!gConn)
  622.         return kNoConnectionErr;
  623.         
  624.     return CMOpen(gConn, gAsyncCalls, gCMCompletion, -1);
  625. }
  626.  
  627. //**********************************************************
  628. //
  629. //    ListenConn - Listen for connection with other copy
  630. //
  631. //**********************************************************
  632. OSErr ListenConn(void)
  633. {
  634.     CMBufferSizes    sizes;
  635.     CMStatFlags        status;
  636.     OSErr            theErr;
  637.         
  638.     if (gConn) {
  639.         theErr = CMStatus(gConn, sizes, &status);
  640.         if (!theErr) {    // async even if other calls are sync
  641.             if (!(status & cmStatusListenPend))
  642.                 theErr = CMListen(gConn, kAsync, gCMCompletion, -1);
  643.             else
  644.                 CMAbort(gConn); // close opened or listening
  645.         }
  646.     } else
  647.         theErr = kNoConnectionErr;
  648.         
  649.     return theErr;
  650. }
  651.  
  652. //**********************************************************
  653. //
  654. //    CloseConn - Close connection with other copy
  655. //
  656. //**********************************************************
  657. OSErr CloseConn(void)
  658. {
  659.     OSErr             theErr;
  660.     CMStatFlags        status;
  661.     CMBufferSizes    sizes;
  662.     
  663.     if (!gConn)                    // close only exists
  664.         return kNoConnectionErr;// and open or opening
  665.  
  666.     // close the connection, but only if it's open or trying to open
  667.     theErr = CMStatus(gConn, sizes, &status);
  668.     if (status & (cmStatusOpen + cmStatusOpening))
  669.         theErr = CMClose(gConn, gAsyncCalls, gCMCompletion, 
  670.             -1, false);
  671.  
  672.     return theErr;
  673. }
  674.  
  675. //**********************************************************
  676. //
  677. //    DisposeConn - Dispose CTB connection
  678. //
  679. //**********************************************************
  680. OSErr DisposeConn(void)
  681. {
  682.     if (!gConn)                // dispose only if exists
  683.         return kNoConnectionErr;
  684.  
  685.     // write resulting new preferences
  686.     WritePrefs(gConn); // may want to complain
  687.     
  688.     CloseConn();    // don't care about error, only 
  689.     HUnlock((Handle)gConn);    // calling in case open
  690.     CMDispose(gConn);
  691.     gConn = nil;
  692.     
  693.     return noErr;
  694. }
  695.  
  696. //**********************************************************
  697. //
  698. //    IdleConn - Performs event loop idle functions 
  699. //                also tells caller if online
  700. //
  701. //**********************************************************
  702. OSErr IdleConn(Boolean *online)
  703. {
  704.     CMStatFlags     status;
  705.     CMBufferSizes    sizes;
  706.     OSErr            theErr;
  707.     
  708.     theErr = noErr;
  709.     
  710.     if (gConn) {
  711.         CMIdle(gConn);    // Accept if we're not already busy
  712.         if (!(theErr = CMStatus(gConn, sizes, &status))) {
  713.             if (status & cmStatusIncomingCallPresent)
  714.                 CMAccept(gConn, true);
  715.             if (status != gPrevStatus) {
  716.                 *online = false;
  717.                 DisableItem(gConnMenu, rConnMItem);
  718.                 DisableItem(gConnMenu, rDiscMItem);
  719.                 DisableItem(gConnMenu, rListenMItem);
  720.                 DisableItem(gConnMenu, rSettingsMItem);
  721.                 if (status & cmStatusOpen) {
  722.                     EnableItem(gConnMenu, rDiscMItem);
  723.                     *online = true;
  724.                 } else if (status & cmStatusListenPend) {
  725.                     EnableItem(gConnMenu, rListenMItem);
  726.                     SetItem(gConnMenu, rListenMItem, 
  727.                         "\pStop Listening");
  728.                 } else {
  729.                     EnableItem(gConnMenu, rConnMItem);
  730.                     EnableItem(gConnMenu, rListenMItem);
  731.                     EnableItem(gConnMenu, rSettingsMItem);
  732.                     SetItem(gConnMenu, rListenMItem, "\pListen");
  733.                 }
  734.                 gPrevStatus = status;
  735.             }
  736.         }
  737.     } else {
  738.         DisableItem(gConnMenu, rConnMItem);
  739.         DisableItem(gConnMenu, rDiscMItem);
  740.         DisableItem(gConnMenu, rListenMItem);
  741.         DisableItem(gConnMenu, rSettingsMItem);
  742.     }
  743.     
  744.     return theErr;
  745. }
  746.  
  747. //**********************************************************
  748. //
  749. // DoConnMenu - Do action that corresponds to menu item
  750. //
  751. //**********************************************************
  752. void DoConnMenu(short itemNumber)
  753. {    
  754.     OSErr theErr;    // you will want to respond to errors
  755.     
  756.     switch(itemNumber) {
  757.         case rConnMItem:
  758.             if (OpenConn())
  759.                 SysBeep(1);
  760.             break;
  761.         case rDiscMItem:
  762.             if (CloseConn())
  763.                 SysBeep(1);
  764.             break;
  765.         case rListenMItem:
  766.             if (ListenConn())
  767.                 SysBeep(1);    // could be cmNotSupported
  768.             break;
  769.         case rSettingsMItem:
  770.             if (theErr = ChooseConn())
  771.                 if (theErr != kNothingChosenErr)
  772.                     SysBeep(1);
  773.             break;
  774.         default:
  775.             break;
  776.     }
  777. }
  778.